/* Copyright 2020 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* ICM-426xx accelerometer and gyroscope for Chrome EC */

#ifndef __CROS_EC_ACCELGYRO_ICM426XX_H
#define __CROS_EC_ACCELGYRO_ICM426XX_H

#include "accelgyro.h"
#include "common.h"

/*
 * 7-bit address is 110100Xb. Where 'X' is determined
 * by the logic level on pin AP_AD0.
 */
#define ICM426XX_ADDR0_FLAGS		0x68
#define ICM426XX_ADDR1_FLAGS		0x69

/* Min and Max sampling frequency in mHz */
#define ICM426XX_ACCEL_MIN_FREQ	3125
#define ICM426XX_ACCEL_MAX_FREQ	MOTION_MAX_SENSOR_FREQUENCY(500000, 100000)
#define ICM426XX_GYRO_MIN_FREQ	12500
#define ICM426XX_GYRO_MAX_FREQ	MOTION_MAX_SENSOR_FREQUENCY(4000000, 100000)

/* Min and Max Accel FS in G */
#define ICM426XX_ACCEL_FS_MIN_VAL	2
#define ICM426XX_ACCEL_FS_MAX_VAL	16

/* Min and Max Gyro FS in dps */
#define ICM426XX_GYRO_FS_MIN_VAL	125
#define ICM426XX_GYRO_FS_MAX_VAL	2000

/* Reg value from Accel FS in G */
#define ICM426XX_ACCEL_FS_TO_REG(_fs)	((_fs) < 2 ? 3 : \
					(_fs) > 16 ? 0 : \
					3 - __fls((_fs) / 2))

/* Accel FSR in G from Reg value */
#define ICM426XX_ACCEL_REG_TO_FS(_reg)	((1 << (3 - (_reg))) * 2)

/* Reg value from Gyro FS in dps */
#define ICM426XX_GYRO_FS_TO_REG(_fs)	((_fs) < 125 ? 4 : \
					(_fs) > 2000 ? 0 : \
					4 - __fls((_fs) / 125))

/* Gyro FSR in dps from Reg value */
#define ICM426XX_GYRO_REG_TO_FS(_reg)	((1 << (4 - (_reg))) * 125)

/* Reg value from ODR in mHz */
#define ICM426XX_ODR_TO_REG(_odr)	((_odr) <= 200000 ? \
					13 - __fls((_odr) / 3125) : \
					(_odr) < 500000 ? 7 : \
					(_odr) < 1000000 ? 15 : \
					6 - __fls((_odr) / 1000000))

/* ODR in mHz from Reg value */
#define ICM426XX_REG_TO_ODR(_reg)	((_reg) == 15 ? 500000 : \
					(_reg) >= 7 ? \
					(1 << (13 - (_reg))) * 3125 : \
					(1 << (6 - (_reg))) * 1000000)

/* Reg value for the next higher ODR */
#define ICM426XX_ODR_REG_UP(_reg)	((_reg) == 15 ? 6 : \
					(_reg) == 7 ? 15 : \
					(_reg) - 1)

/*
 * Register addresses are virtual address on 16 bits.
 * MSB is coding register bank and LSB real register address.
 * ex: bank 4, register 1F => 0x041F
 */
#define ICM426XX_REG_DEVICE_CONFIG	0x0011
#define ICM426XX_SOFT_RESET_CONFIG	BIT(0)

enum icm426xx_slew_rate {
	ICM426XX_SLEW_RATE_20NS_60NS,
	ICM426XX_SLEW_RATE_12NS_36NS,
	ICM426XX_SLEW_RATE_6NS_18NS,
	ICM426XX_SLEW_RATE_4NS_12NS,
	ICM426XX_SLEW_RATE_2NS_6NS,
	ICM426XX_SLEW_RATE_INF_2NS,
};
#define ICM426XX_REG_DRIVE_CONFIG	0x0013
#define ICM426XX_DRIVE_CONFIG_MASK	GENMASK(5, 0)
#define ICM426XX_I2C_SLEW_RATE(_s)	(((_s) & 0x07) << 3)
#define ICM426XX_SPI_SLEW_RATE(_s)	((_s) & 0x07)

/* default int configuration is pulsed mode, open drain, and active low */
#define ICM426XX_REG_INT_CONFIG		0x0014
#define ICM426XX_INT2_LATCHED		BIT(5)
#define ICM426XX_INT2_PUSH_PULL		BIT(4)
#define ICM426XX_INT2_ACTIVE_HIGH	BIT(3)
#define ICM426XX_INT1_LATCHED		BIT(2)
#define ICM426XX_INT1_PUSH_PULL		BIT(1)
#define ICM426XX_INT1_ACTIVE_HIGH	BIT(0)

#define ICM426XX_REG_FIFO_CONFIG	0x0016
#define ICM426XX_FIFO_MODE_BYPASS	(0x00 << 6)
#define ICM426XX_FIFO_MODE_STREAM	(0x01 << 6)
#define ICM426XX_FIFO_MODE_STOP_FULL	(0x02 << 6)

/* data are 16 bits */
#define ICM426XX_REG_TEMP_DATA		0x001D
/* X + Y + Z: 3 * 16 bits */
#define ICM426XX_REG_ACCEL_DATA_XYZ	0x001F
#define ICM426XX_REG_GYRO_DATA_XYZ	0x0025

#define ICM426XX_INVALID_DATA		-32768

#define ICM426XX_REG_INT_STATUS		0x002D
#define ICM426XX_UI_FSYNC_INT		BIT(6)
#define ICM426XX_PLL_RDY_INT		BIT(5)
#define ICM426XX_RESET_DONE_INT		BIT(4)
#define ICM426XX_DATA_RDY_INT		BIT(3)
#define ICM426XX_FIFO_THS_INT		BIT(2)
#define ICM426XX_FIFO_FULL_INT		BIT(1)
#define ICM426XX_AGC_RDY_INT		BIT(0)

/* FIFO count is 16 bits */
#define ICM426XX_REG_FIFO_COUNT		0x002E
#define ICM426XX_REG_FIFO_DATA		0x0030

#define ICM426XX_REG_SIGNAL_PATH_RESET	0x004B
#define ICM426XX_ABORT_AND_RESET	BIT(3)
#define ICM426XX_TMST_STROBE		BIT(2)
#define ICM426XX_FIFO_FLUSH		BIT(1)

#define ICM426XX_REG_INTF_CONFIG0	0x004C
#define ICM426XX_DATA_CONF_MASK		GENMASK(7, 4)
#define ICM426XX_FIFO_HOLD_LAST_DATA	BIT(7)
#define ICM426XX_FIFO_COUNT_REC		BIT(6)
#define ICM426XX_FIFO_COUNT_BE		BIT(5)
#define ICM426XX_SENSOR_DATA_BE		BIT(4)
#define ICM426XX_UI_SIFS_CFG_MASK	GENMASK(1, 0)
#define ICM426XX_UI_SIFS_CFG_SPI_DIS	0x02
#define ICM426XX_UI_SIFS_CFG_I2C_DIS	0x03

enum icm426xx_sensor_mode {
	ICM426XX_MODE_OFF,
	ICM426XX_MODE_STANDBY,
	ICM426XX_MODE_LOW_POWER,
	ICM426XX_MODE_LOW_NOISE,
};
#define ICM426XX_REG_PWR_MGMT0		0x004E
#define ICM426XX_TEMP_DIS		BIT(5)
#define ICM426XX_IDLE			BIT(4)
#define ICM426XX_GYRO_MODE_MASK		GENMASK(3, 2)
#define ICM426XX_GYRO_MODE(_m)		(((_m) & 0x03) << 2)
#define ICM426XX_ACCEL_MODE_MASK	GENMASK(1, 0)
#define ICM426XX_ACCEL_MODE(_m)		((_m) & 0x03)

#define ICM426XX_REG_GYRO_CONFIG0	0x004F
#define ICM426XX_REG_ACCEL_CONFIG0	0x0050
#define ICM426XX_FS_MASK		GENMASK(7, 5)
#define ICM426XX_FS_SEL(_fs)		(((_fs) & 0x07) << 5)
#define ICM426XX_ODR_MASK		GENMASK(3, 0)
#define ICM426XX_ODR(_odr)		((_odr) & 0x0F)

enum icm426xx_filter_bw {
	/* low noise mode */
	ICM426XX_FILTER_BW_ODR_DIV_2 = 0,

	/* low power mode */
	ICM426XX_FILTER_BW_AVG_1X = 1,
	ICM426XX_FILTER_BW_AVG_16X = 6,
};

#define ICM426XX_REG_GYRO_ACCEL_CONFIG0	0x0052
#define ICM426XX_ACCEL_UI_FILT_MASK	GENMASK(7, 4)
#define ICM426XX_ACCEL_UI_FILT_BW(_f)	(((_f) & 0x0F) << 4)
#define ICM426XX_GYRO_UI_FILT_MASK	GENMASK(3, 0)
#define ICM426XX_GYRO_UI_FILT_BW(_f)	((_f) & 0x0F)

#define ICM426XX_REG_FIFO_CONFIG1	0x005F
#define ICM426XX_FIFO_PARTIAL_READ	BIT(6)
#define ICM426XX_FIFO_WM_GT_TH		BIT(5)
#define ICM426XX_FIFO_EN_MASK		GENMASK(3, 0)
#define ICM426XX_FIFO_TMST_FSYNC_EN	BIT(3)
#define ICM426XX_FIFO_TEMP_EN		BIT(2)
#define ICM426XX_FIFO_GYRO_EN		BIT(1)
#define ICM426XX_FIFO_ACCEL_EN		BIT(0)

/* FIFO watermark value is 16 bits little endian */
#define ICM426XX_REG_FIFO_WATERMARK	0x0060

#define ICM426XX_REG_INT_CONFIG1	0x0064
#define ICM426XX_INT_PULSE_DURATION	BIT(6)
#define ICM426XX_INT_TDEASSERT_DIS	BIT(5)
#define ICM426XX_INT_ASYNC_RESET	BIT(4)

#define ICM426XX_REG_INT_SOURCE0	0x0065
#define ICM426XX_UI_FSYNC_INT1_EN	BIT(6)
#define ICM426XX_PLL_RDY_INT1_EN	BIT(5)
#define ICM426XX_RESET_DONE_INT1_EN	BIT(4)
#define ICM426XX_UI_DRDY_INT1_EN	BIT(3)
#define ICM426XX_FIFO_THS_INT1_EN	BIT(2)
#define ICM426XX_FIFO_FULL_INT1_EN	BIT(1)
#define ICM426XX_UI_AGC_RDY_INT1_EN	BIT(0)

#define ICM426XX_REG_INT_SOURCE3	0x0068
#define ICM426XX_UI_FSYNC_INT2_EN	BIT(6)
#define ICM426XX_PLL_RDY_INT2_EN	BIT(5)
#define ICM426XX_RESET_DONE_INT2_EN	BIT(4)
#define ICM426XX_UI_DRDY_INT2_EN	BIT(3)
#define ICM426XX_FIFO_THS_INT2_EN	BIT(2)
#define ICM426XX_FIFO_FULL_INT2_EN	BIT(1)
#define ICM426XX_UI_AGC_RDY_INT2_EN	BIT(0)

#define ICM426XX_REG_WHO_AM_I		0x0075
#define ICM426XX_CHIP_ICM40608		0x39
#define ICM426XX_CHIP_ICM42605		0x42

#define ICM426XX_REG_BANK_SEL		0x0076
#define ICM426XX_BANK_SEL(_b)		((_b) & 0x07)

#define ICM426XX_REG_INTF_CONFIG4	0x017A
#define ICM426XX_I3C_BUS_MODE		BIT(6)
#define ICM426XX_SPI_AP_4WIRE		BIT(1)

#define ICM426XX_REG_INTF_CONFIG5	0x017B
#define ICM426XX_PIN9_FUNC_INT2		(0x00 << 1)
#define ICM426XX_PIN9_FUNC_FSYNC	(0x01 << 1)

#define ICM426XX_REG_INTF_CONFIG6	0x017C
#define ICM426XX_INTF_CONFIG6_MASK	GENMASK(4, 0)
#define ICM426XX_I3C_EN			BIT(4)
#define ICM426XX_I3C_IBI_BYTE_EN	BIT(3)
#define ICM426XX_I3C_IBI_EN		BIT(2)
#define ICM426XX_I3C_DDR_EN		BIT(1)
#define ICM426XX_I3C_SDR_EN		BIT(0)

#define ICM426XX_REG_INT_SOURCE8	0x044F
#define ICM426XX_FSYNC_IBI_EN		BIT(5)
#define ICM426XX_PLL_RDY_IBI_EN		BIT(4)
#define ICM426XX_UI_DRDY_IBI_EN		BIT(3)
#define ICM426XX_FIFO_THS_IBI_EN	BIT(2)
#define ICM426XX_FIFO_FULL_IBI_EN	BIT(1)
#define ICM426XX_AGC_RDY_IBI_EN		BIT(0)

#define ICM426XX_REG_OFFSET_USER0	0x0477
#define ICM426XX_REG_OFFSET_USER1	0x0478
#define ICM426XX_REG_OFFSET_USER2	0x0479
#define ICM426XX_REG_OFFSET_USER3	0x047A
#define ICM426XX_REG_OFFSET_USER4	0x047B
#define ICM426XX_REG_OFFSET_USER5	0x047C
#define ICM426XX_REG_OFFSET_USER6	0x047D
#define ICM426XX_REG_OFFSET_USER7	0x047E
#define ICM426XX_REG_OFFSET_USER8	0x047F

extern const struct accelgyro_drv icm426xx_drv;

void icm426xx_interrupt(enum gpio_signal signal);

#endif /* __CROS_EC_ACCELGYRO_ICM426XX_H */
